/*
 * Copyright Hundsun Technologies Inc. All Rights Reserved.
 */

package module

import (
	"sync"

	"hundsun.com/hsl/hschain/common/config"
	"hundsun.com/hsl/hschain/module/registry"
)

// Status 模块状态
type Status int32

const (
	// StatusError 故障状态（端口冲突、依赖项未启动、启动失败等）
	StatusError Status = iota - 1

	// StatusNotInitialized 尚未初始化
	StatusNotInitialized

	// StatusRunning 运行中
	StatusRunning

	// StatusDown 停止
	StatusDown
)

func (n Status) String() string {
	switch n {
	case StatusError:
		return "StatusError"
	case StatusNotInitialized:
		return "StatusNotInitialized"
	case StatusRunning:
		return "StatusRunning"
	case StatusDown:
		return "StatusDown"
	default:
		return ""
	}
}

// NameEnum 模块名称
type NameEnum int32

const (
	//Unknown 未知模块
	Unknown = -1
	//Consensus 共识
	Consensus NameEnum = iota
	//WorkerExecutor worker
	WorkerExecutor
	//P2p p2p
	P2p
	//RPC rpc
	RPC
	//Blockchain blockchain
	Blockchain
)

const (
	//ConsensusModuleName 共识
	ConsensusModuleName = "consensus"
	//WorkerExecutorModuleName worker
	WorkerExecutorModuleName = "workerExecutor"
	//P2pModuleName p2p
	P2pModuleName = "p2p"
	//RPCModuleName rpc
	RPCModuleName = "rpc"
	//BlockchainModuleName blockchain
	BlockchainModuleName = "blockchain"
)

//GetAllModule 获取所有模块
func GetAllModule() []string {
	return []string{
		ConsensusModuleName, WorkerExecutorModuleName, P2pModuleName, RPCModuleName, BlockchainModuleName,
	}
}

//String ...
func (n NameEnum) String() string {
	switch n {
	case Consensus:
		return ConsensusModuleName
	case WorkerExecutor:
		return WorkerExecutorModuleName
	case P2p:
		return P2pModuleName
	case RPC:
		return P2pModuleName
	case Blockchain:
		return BlockchainModuleName
	default:
		return ""
	}
}

//NewNameEnum 新建枚举
func NewNameEnum(s string) (NameEnum, bool) {
	switch s {
	case ConsensusModuleName:
		return Consensus, true
	case WorkerExecutorModuleName:
		return WorkerExecutor, true
	case P2pModuleName:
		return P2p, true
	case RPCModuleName:
		return RPC, true
	case BlockchainModuleName:
		return Blockchain, true
	default:
		return Unknown, false
	}
}

// Module 模块接口
type Module interface {
	// Name 获取模块名称
	Name() string

	// Startup 启动模块
	// 1. 若启动成功，则状态为StatusRunning
	// 2. 若因无可用依赖项模块实例而无法启动，则一直重试，并打印日志
	// 3. 若端口占用等错误，则状态为StatusError
	Startup(config *config.ModuleConfig) (Status, error)

	// Shutdown 停止模块
	Shutdown() (Status, error)

	// GetRegisteredKV 供模块管理服务获取模块要在注册中心注册的KV
	GetRegisteredKV() (*registry.ModRegKV, error)

	// GetInterestedModules 获取该模块关注的模块名
	GetInterestedModules() []string

	// OnInterestedModuleUp 关注的模块对应的实例上线时回调的方法（不重复处理集群的模块）
	OnInterestedModuleUp(kv *registry.ModRegKV) (Status, error)

	// OnInterestedModuleDown 关注的模块对应的实例下线时回调的方法（不重复处理集群的模块）
	OnInterestedModuleDown(preKV *registry.ModRegKV) (Status, error)

	// GetStatus 获取模块实例状态
	// 若为StatusError，则error中包含具体错误信息
	GetStatus() (Status, error)

	// IsMasterStandbyMode 否是主备模式部署
	IsMasterStandbyMode() bool

	// IsMaster 否是（主备模式）主节点
	//IsMaster() bool

	// CompeteMaster 主备模式时，竞争为主节点，成功则为true
	//CompeteMaster() (bool, error)
}

// ManagementService 模块管理服务，管理当前节点进程上的服务
type ManagementService interface {
	// Init 初始化方法
	Init(commonConf *config.CommonConfig) error

	// serve 启动服务监听 监听客户端发送过来的配置命令
	serve(consoleConfig *config.ConsoleConfig) error

	// handleModulesEnableConfig 校验收到的模块启用配置并将其发送到配置中心
	// 1. 入参所有配置项无错误才调用配置中心客户端
	// 2. 入参中重复配置项将被忽略（依据ModuleID）
	// 3. 配置中心已有相同配置项时，忽略对应入参配置项
	HandleModulesEnableConfig(confSet *config.HostModulesConfigSet) error

	// HandleModulesUpdateConfig 更新启用中的模块的配置（依据ModuleID）
	// ModuleID存在则覆盖更新；不存在则忽略
	HandleModulesUpdateConfig(confSet *config.HostModulesConfigSet) error

	// handleModulesDisableConfig 配置中心删除相应配置项
	HandleModulesDisableConfig(moduleID string) error

	// OnModuleConfigReceived RegistryClient收到新增配置项消息后回调此方法
	OnModuleConfigReceived(conf *config.ModuleConfig) error

	// OnModuleConfigRemoved RegistryClient收到删除配置项消息后回调此方法
	OnModuleConfigRemoved(moduleID string) error

	// OnModuleUp 收到注册中心模块实例上线消息后的回调方法
	OnModuleUp(curKv *registry.ModRegKV) error

	// OnModuleDown 收到注册中心模块实例下线消息后的回调方法
	// 处理 1）依赖下线  2）（主备模式）主模块下线
	OnModuleDown(prevKv *registry.ModRegKV) error

	// refreshManagedModules() 向注册中心刷新当前节点所管理的所有模块实例的状态（即 TTL/心跳，定时调用）
	RefreshManagedModules() error

	// 创建、启动并注册一个模块（封装模块启动流程）
	startupModule(config config.ModuleConfig, commonConf config.CommonConfig, wg *sync.WaitGroup) (Status, error)
}
